import React, { useContext, useEffect, useMemo, useState } from 'react';
import { isNullOrUndefined } from '../../../utils';
import { defaultPagination, defaultRowKey } from '../../config';
import {
  UPDATE_CUROPERATEINFO,
  UPDATE_ERRORS,
  updateErrors,
} from '../../context/table';
import * as utils from '../../utils';
import {
  createTableModalParams,
  createTableSchema,
  getColumnRule,
} from '../../utils';
import validate from '../../utils/json-schema';
import getEditableTableCellComponent from './EditableTableCellCom';

import type { CellContext, ColumnDef } from '@tanstack/react-table';
import type { Context } from '../../context/table';
import type {
  AnyObject,
  ColumnMeta,
  CurOperateInfo,
  RowKey,
  TableMeta,
  TableOnCell,
  TablePagination,
  ValidateError,
} from '../../type';
import type { Props as TableModalProps } from '../TableModal';
import type { Props as SelectPopModalProps } from './TablePopModal';

interface Props<T extends AnyObject, Context = any> {
  isUsePop: boolean;
  info: CellContext<T, unknown>;
  rowKey: RowKey;
  tableColumns: ColumnDef<T>[];
  dataSource: T[];
  pagination: TablePagination;
  context: React.Context<Context>;
  selectTableModal: TableModalProps<T>;
  selectPopModalProps: SelectPopModalProps<T>;
  onCell: TableOnCell<T>;
  setSelectTableModal: React.Dispatch<React.SetStateAction<TableModalProps<T>>>;
  setSelectPopModalProps: React.Dispatch<
    React.SetStateAction<SelectPopModalProps<T>>
  >;
}

function EditableTableCell<T extends AnyObject>(props: Props<T>) {
  const {
    info,
    tableColumns,
    context,
    selectPopModalProps,
    selectTableModal,
    isUsePop,
    pagination,
    rowKey = defaultRowKey,
  } = props;
  const { row, column, table } = info;
  const { original: data, index: rowIndex } = row;
  const columnMeta = (column.columnDef.meta as ColumnMeta<T>) || {};
  const {
    editType,
    editProps = {},
    dataKey,
    align,
    getModalData,
    columns,
    editPropsFn,
    parent,
  } = columnMeta;
  const tableMeta = table.options.meta as TableMeta;
  const initialValue = dataKey ? data[dataKey] : info.getValue();
  const cellValue = !isNullOrUndefined(initialValue) ? initialValue : '';
  const [value, setValue] = useState<any>(cellValue);

  const dataSource = useMemo(() => {
    return utils.handleRowKey(
      props.dataSource.filter((item) => !isNullOrUndefined(item[rowKey])),
      pagination,
      rowKey,
    );
  }, [props.dataSource, rowKey]);

  const {
    table: { errors }, // 当前操作的表格信息
    dispatch,
  } = useContext<Context<T>>(context);
  const tableRules = useMemo(() => getColumnRule(tableColumns), [tableColumns]);

  const getCurOperateInfo = () => ({
    rowIndex,
    columnId: column.id,
    data,
    meta: columnMeta,
    curValue: value,
  });
  const setCurOperateInfo = (curOperateInfo: CurOperateInfo<T>) => {
    dispatch({ type: UPDATE_CUROPERATEINFO, payload: curOperateInfo });
  };
  const onChange = (value) => {
    const curOperateInfo = { ...getCurOperateInfo(), curValue: value };
    if (isUsePop && editType === 'modal') handlePopModal(curOperateInfo);
    setCurOperateInfo(curOperateInfo);
    setValue(value);
  };
  // 编辑保存
  const onBlur = (v?: any) => {
    const columnId = dataKey || column.id;
    const result = v || value;

    onValidate(result);
    if (editType !== 'modal')
      tableMeta.updateCellData(rowIndex, columnId, result);
  };
  // 失焦保存前校验有错误的单元格
  const onValidate = (value: any) => {
    if (
      tableRules[column.id] &&
      errors[rowIndex] &&
      errors[rowIndex][column.id]
    ) {
      const schema = createTableSchema({ [column.id]: tableRules[column.id] }); // 根据当前列生成对应规则
      const validatedErrors = validate({ [column.id]: value }, schema); // 只校验当前单元格值
      const updateError: ValidateError<T> = {
        ...errors[row.index],
        [column.id]: validatedErrors,
      };
      dispatch({
        type: UPDATE_ERRORS,
        payload: updateErrors(row.index, updateError, errors),
      });
    }
  };

  const getModalDataRequest = async (tableModal, setTableModal) => {
    try {
      const { curOperateInfo, pagination } = tableModal;
      const curModalParams = createTableModalParams(
        columns || [],
        curOperateInfo,
      );
      const { current, pageSize } = pagination;
      const { page, size, total, list } = await getModalData(
        current,
        pageSize,
        curModalParams,
      );
      tableModal.dataSource = list;
      tableModal.pagination = { current: page, pageSize: size, total };
      setTableModal(tableModal);
    } catch {
      setTableModal(tableModal);
    }
  };
  const handlePopModal = (curOperateInfo: CurOperateInfo<T>) => {
    const {
      pagination = defaultPagination,
      rowSelection,
      rowKey,
    } = selectPopModalProps;
    const { isCanRepeatSelection } = rowSelection;
    const tableModal = {
      ...selectPopModalProps,
      curOperateInfo,
      visible: true,
      columns,
      pagination,
    };

    if (!isCanRepeatSelection) {
      const selectedRows = dataSource.filter(
        (item) => !isNullOrUndefined(item[rowKey]),
      );
      const selectedKeys = selectedRows.map((item) => item[rowKey]);
      tableModal.rowSelection = {
        ...selectTableModal.rowSelection,
        selectedRows,
        selectCellProps: (data) => ({
          disabled: selectedKeys.includes(data[rowKey]),
        }),
      };
    }

    setCurOperateInfo(curOperateInfo);

    // 弹窗
    if (editType === 'modal') {
      getModalDataRequest(tableModal, props.setSelectPopModalProps);
    }
  };
  const onFocus = () => {
    if (isUsePop && columnMeta.editType === 'modal') {
      const curOperateInfo = getCurOperateInfo();
      handlePopModal(curOperateInfo);
    }
  };

  const onSelectData = () => {
    const curOperateInfo = getCurOperateInfo();
    const { pagination, rowSelection, rowKey } = selectTableModal;
    const { isCanRepeatSelection } = rowSelection;

    const tableModal = {
      ...selectTableModal,
      curOperateInfo,
      visible: true,
      columns,
    };
    if (!isCanRepeatSelection) {
      const selectedRows = dataSource.filter(
        (item) => !isNullOrUndefined(item[rowKey]),
      );
      const selectedKeys = selectedRows.map((item) => item[rowKey]);
      tableModal.rowSelection = {
        ...selectTableModal.rowSelection,
        selectedRows,
        selectCellProps: (data) => ({
          disabled: selectedKeys.includes(data[rowKey]),
        }),
      };
    }
    setCurOperateInfo(curOperateInfo);

    if (getModalData && pagination) {
      getModalDataRequest(tableModal, props.setSelectTableModal);
    } else {
      props.setSelectTableModal(tableModal);
    }
  };

  useEffect(() => setValue(cellValue), [cellValue]);

  const cellMetaProps = useMemo(() => {
    return { ...editProps, ...(editPropsFn && editPropsFn(data, rowIndex)) };
  }, [editProps, editPropsFn, data, rowIndex]);
  const cellProps = useMemo(() => {
    const props = { ...cellMetaProps, align, columnId: column.id, onBlur };
    if (editType === 'modal') props.onSelectData = onSelectData;
    return props;
  }, [cellMetaProps, editType]);

  if (editType && !parent) {
    const {
      onClick: onClickCell,
      onDoubleClick: onDoubleClickCell,
      onMouseEnter: onMouseEnterCell,
      onMouseLeave: onMouseLeaveCell,
    } = (props.onCell && props.onCell(row.original, row.index)) || {};
    const EditableTableCellComponent = getEditableTableCellComponent({
      editType,
    });
    return (
      <EditableTableCellComponent
        {...cellProps}
        value={value}
        onChange={onChange}
        onFocus={onFocus}
        onClick={onClickCell}
        onDoubleClick={onDoubleClickCell}
        onMouseEnter={onMouseEnterCell}
        onMouseLeave={onMouseLeaveCell}
      />
    );
  } else {
    return value;
  }
}

export default EditableTableCell;
